package com.yykj.hospital.server.service.from.template.impl;

import com.google.common.reflect.TypeToken;
import com.yykj.common.enums.ResultEnums;
import com.yykj.common.exception.NormalException;
import com.yykj.common.util.GsonUtil;
import com.yykj.common.util.LocalDateTimeFormatterUtil;
import com.yykj.common.util.LocalObjectUtil;
import com.yykj.common.util.PageRequestUtil;
import com.yykj.common.vo.PageData;
import com.yykj.hospital.server.dto.train.FormTrainFeedbackDetailDTO;
import com.yykj.hospital.server.dto.train.FormTrainLogListDTO;
import com.yykj.hospital.server.form.train.FormTrainContent;
import com.yykj.hospital.server.form.train.FormTrainLogSearchForm;
import com.yykj.hospital.server.form.train.FormTrainSearchForm;
import com.yykj.hospital.server.form.train.TrainFeedbackStatusForm;
import com.yykj.hospital.server.model.entity.form.template.FormTrain;
import com.yykj.hospital.server.model.entity.form.template.FormTrainDetail;
import com.yykj.hospital.server.model.entity.form.template.FormTrainLog;
import com.yykj.hospital.server.model.repository.form.template.FormTrainDetailRepository;
import com.yykj.hospital.server.model.repository.form.template.FormTrainLogRepository;
import com.yykj.hospital.server.model.repository.form.template.FormTrainRepository;
import com.yykj.hospital.server.service.from.template.FormTrainService;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;

@Service
public class FormTrainServiceImpl implements FormTrainService {

    @Override
    public FormTrain editFormTrain(FormTrain formTrain) {
        FormTrain save;
        if (formTrain.getId() != null && formTrain.getId() > 0) {
            save = formTrainRepository.findById(formTrain.getId()).orElseThrow(() -> new NormalException(ResultEnums.NOT_EXIST));
        } else {
            save = new FormTrain();
        }
        LocalObjectUtil.copyNullValue(formTrain, save);
        save = formTrainRepository.save(save);
        return save;
    }

    @Override
    public void deleteFormTrain(Integer id) {
        FormTrain formTrain = formTrainRepository.findById(id).orElseThrow(() -> new NormalException(ResultEnums.NOT_EXIST));
        List<FormTrainDetail> detailList = detailRepository.findAllByHospitalIdAndPid(formTrain.getHospitalId(), formTrain.getId());
        detailRepository.deleteAll(detailList);
        formTrainRepository.delete(formTrain);
    }

    @Override
    public PageData<FormTrain> listFormTrain(FormTrainSearchForm searchForm) {
        if (searchForm.getHospitalId() == null || searchForm.getHospitalId() <= 0) {
            throw new NormalException(ResultEnums.PARAM_ERROR);
        }
        PageRequest pageRequest = PageRequest.of(PageRequestUtil.getPage(searchForm.getCurrentPage())
                , PageRequestUtil.getSize(searchForm.getPageSize())
                , Sort.by(Sort.Order.desc("sorting")));

        Page<FormTrain> all = formTrainRepository.findAll(generateSpecification(searchForm), pageRequest);
        return PageData.formatList(all, searchForm.getCurrentPage(), searchForm.getPageSize());
    }

    @Override
    public FormTrainDetail editFormTrainDetail(FormTrainDetail detail) {
        if (detail.getPid() == null || detail.getPid() <= 0 || StringUtils.isEmpty(detail.getName())) {
            throw new NormalException(ResultEnums.PARAM_ERROR);
        }
        FormTrainDetail save;
        FormTrainDetail oldDetail = detailRepository.findFirstByPidAndName(detail.getPid(), detail.getName());

        if (detail.getId() != null && detail.getId() > 0) {
            if (oldDetail != null && !oldDetail.getId().equals(detail.getId())) {
                throw new NormalException(ResultEnums.TRAIN_DETAIL_NAME_EXIST);
            }
            save = detailRepository.findById(detail.getId()).orElseThrow(() -> new NormalException(ResultEnums.NOT_EXIST));
        } else {
            if (oldDetail != null) {
                throw new NormalException(ResultEnums.TRAIN_DETAIL_NAME_EXIST);
            }
            save = new FormTrainDetail();
        }
        LocalObjectUtil.copyNullValue(detail, save);
        switch (detail.getFieldType()) {
            case select:
            case radio:
            case checkbox:
                if (StringUtils.isEmpty(detail.getValue())) {
                    throw new NormalException(ResultEnums.PARAM_ERROR, "select radio checkbox 时，value必输");
                }
                break;
        }

        save = detailRepository.save(save);
        return save;
    }

    @Override
    public void deleteFormTrainDetail(Integer id) {
        detailRepository.deleteById(id);
    }

    @Override
    public List<FormTrainDetail> listFormTrainDetail(Integer id) {
        return detailRepository.findAllByPid(id);
    }

    @Override
    public void submitTrain(FormTrainContent content) {
        if (content.getHospitalId() == null || content.getHospitalId() <= 0
                || content.getTrainId() == null || content.getTrainId() <= 0) {
            throw new NormalException(ResultEnums.PARAM_ERROR);
        }
        FormTrainLog log = new FormTrainLog();
        BeanUtils.copyProperties(content, log);
        List<FormTrainDetail> trainDetailList = detailRepository.findAllByHospitalIdAndPid(content.getHospitalId(), content.getTrainId());
        for (FormTrainDetail detail : trainDetailList) {
            if (Boolean.TRUE.equals(detail.getMust())) {
                String val = content.getContent().get(detail.getName());
                if (StringUtils.isEmpty(val)) {
                    throw new NormalException(ResultEnums.TRAIN_VALUE_NOT_SET_ERROR, ResultEnums.TRAIN_VALUE_NOT_SET_ERROR.getMessage() + detail.getName());
                }
            }
        }

        log.setContent(GsonUtil.toJson(content.getContent()));
        trainLogRepository.save(log);

    }

    @Override
    public PageData<FormTrainLogListDTO> listFormTrainFeedbackLog(FormTrainLogSearchForm form) {
        if (form.getHospitalId() == null || form.getHospitalId() <= 0) {
            throw new NormalException(ResultEnums.PARAM_ERROR);
        }
        PageRequest pageRequest = PageRequest.of(PageRequestUtil.getPage(form.getCurrentPage()),
                PageRequestUtil.getSize(form.getPageSize()), Sort.by(Sort.Order.desc("addTime")));
        Page<FormTrainLog> list = trainLogRepository.findAll(generateFormTrainLogSpecification(form), pageRequest);
        PageData<FormTrainLog> pageDateLog = PageData.formatList(list, form.getCurrentPage(), form.getPageSize());
        PageData<FormTrainLogListDTO> result = new PageData<>();
        List<FormTrainLogListDTO> resultList = new ArrayList<>();
        for (FormTrainLog log : list.getContent()) {
            FormTrainLogListDTO dto = new FormTrainLogListDTO();
            BeanUtils.copyProperties(log, dto);
            if (StringUtils.isEmpty(log.getContent())) {
                dto.setUserValue(new HashMap<>());
            } else {
                dto.setUserValue(GsonUtil.fromJson(log.getContent(), new TypeToken<Map<String, String>>() {
                }.getType()));
            }
            resultList.add(dto);
        }
        BeanUtils.copyProperties(pageDateLog, result, "list");
        result.setList(resultList);

        return result;
    }

    @Override
    public List<FormTrainFeedbackDetailDTO> listFormTrainFeedbackDetail(Integer logId) {
        FormTrainLog formTrainLog = trainLogRepository.findById(logId).orElseThrow(() -> new NormalException(ResultEnums.NOT_EXIST));
        List<FormTrainDetail> trainDetailList = detailRepository.findAllByHospitalIdAndPid(formTrainLog.getHospitalId(), formTrainLog.getTrainId());
        List<FormTrainFeedbackDetailDTO> result = new ArrayList<>();
        Map<String, String> map = new HashMap<>();
        if (!StringUtils.isEmpty(formTrainLog.getContent())) {
            map = GsonUtil.fromJson(formTrainLog.getContent(), new TypeToken<Map<String, String>>() {
            }.getType());
        }
        for (FormTrainDetail detail : trainDetailList) {
            FormTrainFeedbackDetailDTO dto = new FormTrainFeedbackDetailDTO();
            BeanUtils.copyProperties(detail, dto);
            dto.setUserValue(map.get(dto.getName()));
            result.add(dto);
        }
        return result;
    }

    @Override
    public void changeStatusTrainFeedback(TrainFeedbackStatusForm form) {
        if (form.getIds() == null || form.getIds().size() == 0 || form.getStatus() == null) {
            throw new NormalException(ResultEnums.PARAM_ERROR);
        }
        List<FormTrainLog> formTrainLogList = trainLogRepository.findAllByIdIn(form.getIds());
        for (FormTrainLog formTrainLog : formTrainLogList) {
            formTrainLog.setStatus(form.getStatus());
            formTrainLog.setFinishTime(LocalDateTime.now());
            formTrainLog.setRemark(form.getRemark());
        }
        trainLogRepository.saveAll(formTrainLogList);
    }

    @Override
    public Workbook exportTrainFeedbackLog(FormTrainLogSearchForm form) {
        List<FormTrainLog> logList = trainLogRepository.findAll(generateFormTrainLogSpecification(form));
        Set<Integer> trainIds = new HashSet<>();
        for (FormTrainLog log : logList) {
            trainIds.add(log.getTrainId());
        }

        Map<Integer, FormTrain> trainMap = new HashMap<>();
        Map<Integer, List<FormTrainFeedbackDetailDTO>> logDetailDTO = new HashMap<>();
        Map<Integer, List<String>> trainTitle = new HashMap<>();
        Map<Integer, List<FormTrainDetail>> trainDetailMap = new HashMap<>();

        List<FormTrain> trainList = formTrainRepository.findAllByIdIn(trainIds);
        for (FormTrain train : trainList) {
            trainMap.put(train.getId(), train);
            trainDetailMap.put(train.getId(), detailRepository.findAllByPid(train.getId()));
        }
        HSSFWorkbook workbook = new HSSFWorkbook();
        for (Integer trainId : trainIds) {
            FormTrain train = trainMap.get(trainId);
            List<String> titleList = trainTitle.get(trainId);
            List<FormTrainDetail> trainDetailList = trainDetailMap.get(trainId);
            if (titleList == null) {
                titleList = getTrainTitle(trainDetailList);
                trainTitle.put(trainId, titleList);
            }
            List<List<FormTrainFeedbackDetailDTO>> dataList = new ArrayList<>();
            for (FormTrainLog log : logList) {
                if (log.getTrainId().equals(trainId)) {
                    List<FormTrainFeedbackDetailDTO> currentLineData = new ArrayList<>();

                    currentLineData.add(FormTrainFeedbackDetailDTO.builder().userValue(log.getPatientName()).build());//患者名称
                    currentLineData.add(FormTrainFeedbackDetailDTO.builder().userValue(log.getAddTime()
                            .format(LocalDateTimeFormatterUtil.getLocalDateTimeFormatter())).build());//提交时间
                    currentLineData.add(FormTrainFeedbackDetailDTO.builder().userValue(log.getStatus() ? "已处理" : "未处理").build());//处理状态
                    if (log.getStatus() != null && log.getStatus()) {
                        currentLineData.add(FormTrainFeedbackDetailDTO.builder().userValue(log.getFinishTime()
                                .format(LocalDateTimeFormatterUtil.getLocalDateTimeFormatter())).build());//处理时间

                    } else {
                        currentLineData.add(FormTrainFeedbackDetailDTO.builder().userValue("-").build());//处理时间
                    }

                    Map<String, String> map = GsonUtil.fromJson(log.getContent(), new TypeToken<Map<String, String>>() {
                    }.getType());
                    for (FormTrainDetail detail : trainDetailList) {
                        FormTrainFeedbackDetailDTO dto = new FormTrainFeedbackDetailDTO();
                        BeanUtils.copyProperties(detail, dto);
                        dto.setUserValue(map.get(dto.getName()));
                        currentLineData.add(dto);
                    }
                    dataList.add(currentLineData);
                }
            }

            createSheet(workbook, train.getTitle(), titleList, dataList);
        }
        return workbook;
    }

    private List<String> getTrainTitle(List<FormTrainDetail> detailList) {
        List<String> title = new ArrayList<>();
        title.add("患者名称");
        title.add("提交时间");
        title.add("处理状态");
        title.add("处理时间");
        for (FormTrainDetail detail : detailList) {
            title.add(detail.getLabel());
        }
        return title;
    }


    private HSSFSheet createSheet(HSSFWorkbook workbook, String sheetName, List<String> title, List<List<FormTrainFeedbackDetailDTO>> dataList) {
        HSSFSheet sheet = workbook.createSheet(sheetName);
        int rowIndex = 0;
        int colIndex = 0;

        HSSFRow row = sheet.createRow(rowIndex++);
        for (String str : title) {
            HSSFCell cell = row.createCell(colIndex++);
            cell.setCellValue(str);
        }
        for (List<FormTrainFeedbackDetailDTO> list : dataList) {
            row = sheet.createRow(rowIndex++);
            colIndex = 0;
            for (FormTrainFeedbackDetailDTO detailDTO : list) {
                HSSFCell cell = row.createCell(colIndex++);
                cell.setCellValue(detailDTO.getUserValue());
            }
        }

        return sheet;
    }


    private Specification<FormTrainLog> generateFormTrainLogSpecification(FormTrainLogSearchForm form) {
        return new Specification<FormTrainLog>() {
            @Override
            public Predicate toPredicate(Root<FormTrainLog> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
                List<Predicate> predicateList = new ArrayList<>();
                predicateList.add(builder.equal(root.get("hospitalId"), form.getHospitalId()));
                if (form.getTrainId() != null) {
                    predicateList.add(builder.equal(root.get("trainId"), form.getTrainId()));
                }
                if (!StringUtils.isEmpty(form.getUserId())) {
                    predicateList.add(builder.equal(root.get("userId"), form.getUserId()));
                }
                if (!StringUtils.isEmpty(form.getUsername())) {
                    predicateList.add(builder.equal(root.get("username"), form.getUsername()));
                }

                if (!StringUtils.isEmpty(form.getPatientId())) {
                    predicateList.add(builder.equal(root.get("patientId"), form.getPatientId()));
                }
                if (!StringUtils.isEmpty(form.getPatientName())) {
                    predicateList.add(builder.like(root.get("patientName"), "%" + form.getPatientName() + "%"));
                }
                if (!StringUtils.isEmpty(form.getStartTime())) {
                    LocalDateTime startTime = LocalDateTime.of(
                            LocalDate.parse(form.getStartTime(), LocalDateTimeFormatterUtil.getLocalDateFormatter()), LocalTime.MIN);
                    predicateList.add(builder.greaterThanOrEqualTo(root.get("addTime"), startTime));
                }
                if (!StringUtils.isEmpty(form.getEndTime())) {
                    LocalDateTime endTime = LocalDateTime.of(
                            LocalDate.parse(form.getEndTime(), LocalDateTimeFormatterUtil.getLocalDateFormatter()), LocalTime.MAX);
                    predicateList.add(builder.lessThanOrEqualTo(root.get("addTime"), endTime));
                }
                if (form.getStatus() != null) {
                    predicateList.add(builder.equal(root.get("status"), form.getStatus()));
                }
                Predicate[] predicates = new Predicate[predicateList.size()];
                return query.where(predicateList.toArray(predicates)).getRestriction();
            }
        };
    }


    private Specification<FormTrain> generateSpecification(FormTrainSearchForm form) {
        return new Specification<FormTrain>() {
            @Override
            public Predicate toPredicate(Root<FormTrain> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
                List<Predicate> predicateList = new ArrayList<>();
                predicateList.add(builder.equal(root.get("hospitalId"), form.getHospitalId()));
                if (form.getTypeId() != null && form.getTypeId() > 0) {
                    predicateList.add(builder.equal(root.get("typeId"), form.getTypeId()));
                }
                Predicate[] predicates = new Predicate[predicateList.size()];
                return query.where(predicateList.toArray(predicates)).getRestriction();
            }
        };
    }

    private FormTrainRepository formTrainRepository;

    private FormTrainDetailRepository detailRepository;

    private FormTrainLogRepository trainLogRepository;

    @Autowired
    public void setFormTrainRepository(FormTrainRepository formTrainRepository) {
        this.formTrainRepository = formTrainRepository;
    }

    @Autowired
    public void setDetailRepository(FormTrainDetailRepository detailRepository) {
        this.detailRepository = detailRepository;
    }

    @Autowired
    public void setTrainLogRepository(FormTrainLogRepository trainLogRepository) {
        this.trainLogRepository = trainLogRepository;
    }
}
